//ss
package compiler.antlr.helloworld;//ge compiler.antlr.helloworld;


import java.util.Arrays;
import java.util.Map;

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
import org.antlr.v4.runtime.tree.RuleNode;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;

import compiler.antlr.Tool;
import compiler.antlr.helloworld.AlexParser.AssignStatmentContext;
import compiler.antlr.helloworld.AlexParser.ExpressionContext;
import compiler.antlr.helloworld.AlexParser.ProgContext;
import compiler.antlr.helloworld.AlexParser.StatmentContext;
import core.LoggerSupport;

/**
 * 暂时不支持一个函数里面声明另外一个函数
 * g4语法文件暂时不支持
 * @author Alex
 *
 */
class FuncDeclare{
	private StatmentContext funcStatment;
	private int paramNumNeeded;
	public FuncDeclare(StatmentContext sc,int num) {
		this.funcStatment = sc;
		this.paramNumNeeded = num;
	}
	public StatmentContext getFuncStatment() {
		return funcStatment;
	}
	public int getParamNumNeeded() {
		return paramNumNeeded;
	}
	
}
class ExecContext{
	private Map<String,String> mapping = null;
	private static Map<String,FuncDeclare> funcsDecl = null;
	ExecContext parent = null;
	private String name = null;
	public String getName(){
		return name;
	}
	public ExecContext() {
		this.mapping = Maps.newHashMap();
		if(funcsDecl==null){
			funcsDecl = Maps.newHashMap();
		}
	}
	public ExecContext(ExecContext parent,String name){
		this();
		this.parent = parent;
		this.name = name;
	}
	public String set(String key,String value){
		mapping.put(key, value);
		return value;
	}
	public FuncDeclare setFuncDecl(String key,FuncDeclare value){
		funcsDecl.put(key, value);
		return value;
	}
	public Object getFuncByName(String key){
		return funcsDecl.get(key);
	}
	public String get(String key){
		return mapping.get(key);
	}
	public String getOrElse(String key,String value){
		return mapping.getOrDefault(key,value);
	}
	public boolean contains(String key){
		return mapping.containsKey(key);
	}
	public String getRecursive(String key){
		return mapping.get(key) == null?(parent == null?null: parent.get(key)):mapping.get(key);
	}
	
	public static ExecContext copyFrom(ExecContext from,String newName,String []keys){
		ExecContext ec = getNew(newName);
		Arrays.asList(keys).forEach((x)->{
			ec.set(x, from.get(x));
		});
		ec.parent = from.parent;
		return ec;
	}
	public String setRecursive(String key,String value){
		//当前有
		if(this.get(key)!=null){
			return this.set(key, value);
		}else{
			//当前没有
			if(parent == null){
				return this.set(key, value);
			}else{
				return parent.set(key, value);
			}
		}
	};
	
	public Map<String,String> getMap(){
		return mapping;
	}
	
	public static ExecContext getNew(String name,ExecContext parent){
		return new ExecContext(parent,name);
	}
	public static ExecContext getNew(String name){
		return getNew(name,null);
	}
	public static ExecContext getNew(ExecContext parent){
		return getNew(null,parent);
	}
	@Override
	public String toString() {
		return name + " --- " + mapping.toString();
	}
	
}
class Listener extends AlexBaseListener{
	Logger log = LoggerFactory.getLogger(this.getClass());
	String []rules;
	public Listener(String [] rules) {
		this.rules = rules;
	}
	@Override
	public void enterProg(ProgContext ctx) {
		log.info("enter prog");
		super.enterProg(ctx);
	}
}
class Intepreter extends LoggerSupport implements ParseTreeVisitor<String>{
	String []rules;
	public Intepreter(String []rules) {
		this.rules = rules;
	}
	@Override
	public String visit(ParseTree tree) {
		logger.info("{}",tree);
		return null;
	}
	//	main
	@Override
	public String visitChildren(RuleNode node) {
		String ruleName = rules[node.getRuleContext().getRuleIndex()];
		if(ruleName.equals("prog")){
			//全局执行环境
			ExecContext global = ExecContext.getNew("global", null);
			logger.info("begin execting prog");
			handleListStatments(node, global);
			global.getMap().keySet().forEach(x->System.out.println(String.format("key:%s,value:%s",x,global.getMap().get(x))));
		}else{
			logger.info("RuleName :{} isn't prog ,exit",ruleName);
		}
		return null;
	}
	//执行root节点下面的Statement序列
	String handleListStatments(ParseTree root,ExecContext ec){
		int k = root.getChildCount();
		for(int i = 0;i<k;i++){
			ParseTree pt = root.getChild(i);
			handle(pt,ec);
		}
		return "";
	}
	void handle(ParseTree pt,ExecContext ec){
		if(pt instanceof ParserRuleContext){
			ParserRuleContext prc = (ParserRuleContext)pt;
			String ruleName = rules[prc.getRuleContext().getRuleIndex()];
			if(ruleName.equals("statment")){
				assert prc.getChildCount()==1;
				ParserRuleContext concreteStat = tree2PRC(prc.getChild(0));
				String conName = rules[concreteStat.getRuleIndex()];
				handleconcreteStatment(concreteStat, conName,ec);
			}else if(ruleName.equals("functionDecl")){
				//处理函数参数个数,函数名称
				String funcName = paylod2String(prc.getChild(1));
				//有没有参数会影响statment出现位置,所以这里
				for(int k=0,c = prc.getChildCount();k<c;k++){
					if(prc.getChild(k) instanceof StatmentContext){
						int lb = 2;
						int rb = lb+1;
						for(int j=lb+1;j<prc.getChildCount();j++){
							if(prc.getChild(j) instanceof TerminalNode && paylod2String(prc.getChild(j)).equals(")")){
								rb = j;
							}
						}
						int parmsNum = rb-lb-1;
						ec.setFuncDecl(funcName,new FuncDeclare((StatmentContext)prc.getChild(k),parmsNum));
						break;
					}
				}
			}
		}else if(pt instanceof TerminalNodeImpl){
		}else{
			logger.info("{}",pt);
		}

	}
	boolean isVariable(TerminalNode tn){
		String literal = paylod2String(tn);
		try{
			Integer.parseInt(literal);
			return false;
		}catch(Exception e){
			return true;
		}
		
	}
	String handleconcreteStatment(ParserRuleContext prc,String ruleName,ExecContext ec){
		if(ruleName.equals("assignStatment")){
			//对赋值(声明)语句的处理,要具体一般性.力求无论语句每个被执行的语句出面在哪个位置都应该能正常执行
			//赋值(声明)语句出现位置可能是变量声明,变量修改,或者for循环中,都会执行到这个分支,所以要注意判断当前是变量的声明及初始化
			//还是对已经存在的变量进行修改其值
			AssignStatmentContext asc = (AssignStatmentContext)prc;
			//child[0]是赋值号左侧部分
			String id = ((Token)asc.getChild(0).getPayload()).getText();
//			ParseTree pt = asc.getChild(1);//= skip
			//字面量或者是变量需要判断,child[2]是赋值号右侧的部分
			ParseTree secC = asc.getChild(2);
			if(secC instanceof TerminalNode){
				TerminalNode tnSecC = (TerminalNode)secC;
				boolean variable = isVariable(tnSecC);
				String literal = null;
				if(variable){
					literal = ec.getRecursive(paylod2String(tnSecC));
				}else{
					literal = paylod2String(tnSecC);
				}
				if(ec.getRecursive(id) == null){
					//父EC中不存在,id为局部变量因此要把当前值保存到执行环境中去.
					ec.set(id, literal);
				}else{
					//父环境中存在说明是修改父环境中的值
					ec.setRecursive(id, literal);
				}
			
			}else{
				ExpressionContext exprcon = (ExpressionContext)asc.getChild(2);//ExpressionContext
				String result = calcExpr(exprcon,null);
				ec.setRecursive(id, result);
			}
			return "assignResult";
		}else if(ruleName.equals("ifStatment")){
//			String ifStr = paylod2String(prc, 0); //skip ifStr == "if"
			ParserRuleContext ifexpr = (ParserRuleContext) prc.getChild(2);
			String result = calcExpr((ExpressionContext)ifexpr,null);
			boolean ifExprResult = !result.equals("0");
			boolean hasElse = prc.getChildCount() >3;
			if(!ifExprResult && !hasElse) return "empty";
			else if(ifExprResult){
				//返回第一个分支的结果
				//if自己也形成一个作用域
				ExecContext ifEc = ExecContext.getNew(prc.getStart().getText(), ec);
				ParserRuleContext ifRoot = tree2PRC(prc.getChild(4));
				//FIXME 符号是否在父EC中出现 ,应该在其它类型staement中执行时候判断
				handle(ifRoot, ifEc);
				return "ifResult";
			}else{
				//返回else的结果
				ExecContext ifEc = ExecContext.getNew(prc.getStart().getText(), ec);
				ParserRuleContext ifRoot = tree2PRC(prc.getChild(6));
				handle(ifRoot, ifEc);
//				handleListStatments(ifRoot, ifEc);
				return "elseResult";
			}
		}else if(ruleName.equals("functionInvokStatment")){
			String funcName = paylod2String(prc,0);
			int lb = 1;
			int rb = lb+1;
			for(int k=lb+1;k<prc.getChildCount();k++){
				if(prc.getChild(k) instanceof TerminalNode && paylod2String(prc.getChild(k)).equals(")")){
					rb = k;
					break;
				}
			}
			int parmsNum = rb-lb-1;
			ExecContext funcEC = null;
			//形成参数的新的执行环境,函数执行环境的父环境为当前
			if(parmsNum==0){
				funcEC = ExecContext.getNew(funcName+"-EC", ec); 
			}else{
				String paraNames [] = new String[parmsNum];
				for(int k=lb+1;k<rb;k++){
					paraNames[k-lb-1] = paylod2String(prc.getChild(k));
				}
				funcEC = ExecContext.copyFrom(ec, funcName+"-", paraNames);
			}
			logger.info("invoke func name :{} ,lb:{},rb{}",funcName,lb,rb);
			FuncDeclare func = (FuncDeclare)ec.getFuncByName(funcName);
			if(parmsNum != func.getParamNumNeeded())throw new RuntimeException("parmas not match");
			handle(func.getFuncStatment(), funcEC);
		}else if(ruleName.equals("forStatment")){
			//只支持for(init;condition;stat){} 简化编程及处理
			ParserRuleContext forInit = (ParserRuleContext)prc.getChild(2);
			//把变量声明添加到for中
			String id = paylod2String(forInit,0);
			//for循环自己形成一个作用域
			ExecContext forInitEC = ExecContext.getNew("forInit-EC", ec);
			forInitEC.set(id, "new");
									
			handleconcreteStatment(forInit,"assignStatment",forInitEC);
//			String initVal = forInitEC.get(id); //for init val
						//for condition expr
			ExpressionContext conditionExprCtx = (ExpressionContext)prc.getChild(3);
			String condRes = calcExpr(conditionExprCtx, forInitEC);
			
			//for update statement
			ExpressionContext statCtx = (ExpressionContext) prc.getChild(5);
			int left = 0;
			int right = 0;
			for(int j=0,c=prc.getChildCount();j<c;j++){
				if(!(prc.getChild(j) instanceof TerminalNode)){
					continue;
				}
				left = paylod2String(prc,j).equals("{")?j:left;
				right = paylod2String(prc,j).equals("}")?j:right;
			}
			left ++;
			
			while(condRes.equals("true")){
				for(int k = left;k<right;k++){
					StatmentContext sc = (StatmentContext)prc.getChild(k);
					//FIXME: FOR语句里面的赋值或者修改语句有问题
					handle(sc, forInitEC);
				}
				logger.info("for exec ");
				calcExpr(statCtx, forInitEC);
				condRes = calcExpr(conditionExprCtx, forInitEC);
			}
			logger.info("{}",forInitEC);		
		}else{
			logger.info(ruleName);
		}
		
		return "";
	}
	Integer resolve(String payload,ExecContext exprEC){
		if(exprEC == null || exprEC.get(payload) == null){
			return Integer.parseInt(payload);
		}else{
			return Integer.parseInt(exprEC.get(payload));
		}
	}
	String calcExpr(ExpressionContext ec,ExecContext exprEC){
		//要判断是几元运算符,根据childCount返回的结果可以确定,2元的话有3个child.
		int k = ec.getChildCount();
		if(k==1){
			return paylod2String(ec,0);
		}else if(k==2){
			//++ or  --
			String symbol = null;
			// id
			String varName = null;
			boolean suffix = false;
			int val = 0;
			varName = paylod2String(ec,0);
			if(exprEC.contains(varName)){
				//说明是后缀运算符
				val = resolve(varName, exprEC);
				suffix = true;
				symbol = paylod2String(ec.getChild(1));
			}else{
				varName = paylod2String(ec,1);
				//说明是前缀
				val = resolve(varName,exprEC);
				suffix = false;
				symbol = paylod2String(ec.getChild(0));
			}
			switch(symbol){
				case "++":
					exprEC.set(varName, String.valueOf(val+1));
					if(suffix)
						return String.valueOf(val);
					else
						return exprEC.get(varName);
				case "--":
					exprEC.set(varName, String.valueOf(val-1));
					if(suffix){
						return String.valueOf(val);
					}else{
						return exprEC.get(varName);
					}
			}
		} else if(k>=1){
			String symbol = paylod2String(ec, 1);
//			logger.info("symbol {} ",symbol);
			ParseTree left = ec.getChild(0);
			ParseTree right = ec.getChild(2);
			String leftRes = null;
			String rightRes = null;
			if(left  instanceof TerminalNode)
				leftRes = paylod2String(left);
			else
				leftRes = calcExpr((ExpressionContext)left,exprEC);
			if(right  instanceof TerminalNode)
				rightRes = paylod2String(right);
			else
				rightRes = calcExpr((ExpressionContext)right,exprEC);
//			String varVal = exprEC.get(leftRes);
//			if(varVal!=null){
//				
//			}
			int l = resolve(leftRes,exprEC);
			int r = resolve(rightRes,exprEC);
//			logger.info("{} {} {}",l,symbol,r);
			switch (symbol) {
				case "+":
					return String.valueOf(l+r);
				case "-":
					return String.valueOf(l-r);
				case "*":
					return String.valueOf(l*r);
				case "/":
					return String.valueOf(l/r);
				case "<":
					return String.valueOf(l<r);
				default:
					return "1";
			}
		}
		return "";
	}
	String paylod2String(ParseTree pt,int idx){
		return ((Token)pt.getChild(idx).getPayload()).getText();
	}
	String paylod2String(ParseTree pt){
		return ((Token)pt.getPayload()).getText();
	}

	@Override
	public String visitTerminal(TerminalNode node) {
		return null;
	}

	@Override
	public String visitErrorNode(ErrorNode node) {
		return null;
	}
	void print(ParserRuleContext prc,int level){
		String ruleName  =  rules[prc.getRuleIndex()];
		logger.info("level: {} ruleName-->{}",level,ruleName);
	}
	void print(TerminalNodeImpl tn,int level){
		String name = tn.getSymbol().getText();
		logger.info("level: {} TER-->{}",level,name);
	}
	ParserRuleContext tree2PRC(ParseTree pt){
		return (ParserRuleContext)pt;
	}
	int getOperandNum(String sym){
		return 2;
	}
}

public class Bootstrap {
	static Logger log = LoggerFactory.getLogger(Bootstrap.class);
	public static void main(String[] args) throws Exception {
		boolean flag = false;
		if(flag){
			Tool.main2(new String[]{"compiler.antlr.helloworld.Alex","prog","compiler/antlr/helloworld/alex.txt"});
			return;
		}
		AlexParser ap = Tool.getParser("compiler/antlr/helloworld/alex.txt", AlexLexer.class, AlexParser.class);
		String rules [] = ap.getRuleNames();
		
		ParserRuleContext prc = ap.prog();
	
		prc.accept(new Intepreter(rules));
	}
}
